﻿using System;
using System.Collections.Generic;

namespace Codemonk.Common.Caching
{
    public sealed class DictionaryCache<TKey, TValue> : ICacheCollection<TKey, TValue>
    {
        private static readonly object sync = new object();

        private TValue nullKeyValue = default(TValue);

        private IDictionary<TKey, TValue> cache;

        private IDictionary<TKey, TValue> Cache
        {
            get
            {
                if (cache == null)
                {
                    lock (sync)
                    {
                        if (cache == null) cache = new Dictionary<TKey, TValue>();
                    }
                }
                return cache;
            }
        }

        public bool ContainsKey(TKey key)
        {
            if (key == null) { return nullKeyValue != null; }
            return Cache.ContainsKey(key);
        }

        public void Set(TKey key, TValue value)
        {
            lock (sync)
            {
                if (key == null) nullKeyValue = value;
                else Cache[key] = value;
            }
        }

        public bool TryGet(TKey key, out TValue value)
        {
            if (key == null) { value = nullKeyValue; return nullKeyValue != null; }
            return Cache.TryGetValue(key, out value);
        }

        public int Count
        {
            get { return Cache.Count; }
        }

        public IEnumerable<TKey> Keys
        {
            get
            {
                foreach (var item in Cache)
                {
                    yield return item.Key;
                }
                if (nullKeyValue != null) yield return default(TKey);
            }
        }

        public void Clear()
        {
            lock (sync)
            {
                Cache.Clear();
            }
        }

        public void Set(ICacheCollection<TKey, TValue> cache)
        {
            if (cache == null || cache.Count == 0) return;
            TValue value;
            var keys = cache.Keys;
            foreach (var key in keys)
            {
                if (cache.TryGet(key, out value))
                    Set(key, value);
            }
        }

        public IEnumerator<System.Tuple<TKey, TValue>> GetEnumerator()
        {
            if (Count == 0) yield return null;
            foreach (var item in Cache) yield return new Tuple<TKey, TValue>(item.Key, item.Value);
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return Cache.GetEnumerator();
        }
    }
}
